/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.media.opengl.GL;
import object.Color;
import object.Edge;
import object.Point;
import object.Polygon;
import object.Zbuffer;

/**
 *
 * @author dheath
 */
public class ScanConvert
{

    private boolean draw;
    private List<Edge>[] GET;
    private List<Edge> AET;
    private float Y;
    private float polyLow;
    private float polyHigh;
   
    public ScanConvert()
    {
        draw = false;
        polyLow = 100000;
        polyHigh = -100000;
    }

    public void scanConvert(Polygon poly,Polygon world, GL gl, Zbuffer zbuffer, Point lookFrom, boolean shading)
    {
        try
        {

            for (Point p : poly.getPoints())
            {
                if (p.getY() < polyLow)
                {
                    polyLow = p.getY();
                }
                if (p.getY() > polyHigh)
                {
                    polyHigh = p.getY();
                }
            }

            GET = new List[(int) (polyHigh - polyLow + 1)];
            for (int i = 0; i < GET.length; i++)
            {
                GET[i] = new ArrayList();
            }

            AET = new ArrayList();

            for (int i = 0; i < poly.getPoints().size(); i++)
            {
                Point a = poly.getPoints().get(i);
                Point b;
                Point c = world.getPoints().get(i);
                Point d;
                
                if (i + 1 >= poly.getPoints().size())
                {
                    b = poly.getPoints().get(0);
                    d = world.getPoints().get(0);
                } else
                {
                    b = poly.getPoints().get(i + 1);
                    d = world.getPoints().get(i+1);
                }

                Edge edge = new Edge(a, b,c,d);
                if (!edge.isHorizontal())
                {
                    GET[(int) (edge.getYmin() - polyLow)].add(edge);
                }
            }

            Y = polyLow;

            while ((!getIsEmpty(GET) || !AET.isEmpty()) && ((int) (Y - polyLow) < GET.length))
            {
                /*if((int) (Y - polyLow) >= GET.length)
                {
                    Y = Y-1;
                }*/
                List get = GET[(int) (Y - polyLow)];
                AET.addAll(get);
                get.clear();

                //ERROR????
                List<Edge> indexes = new ArrayList();
                for (int i = 0; i < AET.size(); i++)
                {
                    if (AET.get(i).getYmax() == Y)//float comparison???
                    {
                        indexes.add(AET.get(i));
                    }
                }
                for(Edge e : indexes)
                {
                    
                    AET.remove(e);
                }
                
                
                if (!AET.isEmpty())
                {
                    Collections.sort(AET);

                    //fill colors
                    draw = true;

                    for (int i = 0; i + 1 < AET.size(); i++)
                    {
                        Edge a = AET.get(i);
                        Edge b = AET.get(i + 1);
                        
                        float z = a.getZ();
                        float zmx = a.horizontalSlope(a, b);
                        
                        float nX = a.getNX();
                        float nY = a.getNY();
                        float nZ = a.getNZ();
                        float mnX = a.getNxSLope(a, b);
                        float mnY = a.getNySLope(a, b);
                        float mnZ = a.getNzSLope(a, b);
                        
                        float wX = a.getWX();
                        float wY = a.getWY();
                        float wZ = a.getWZ();
                        float mwX = a.getWxSLope(a, b);
                        float mwY = a.getWySLope(a, b);
                        float mwZ = a.getWzSLope(a, b);
                        
                        if (draw)
                        {
                            for (float X = a.getX(); X < b.getX(); X = X + 1)
                            {
                                if(X < 800 && Y < 800 && X >= 0 && Y >= 0)
                                {
                                if(zbuffer.testAndSet((int)X, (int)Y, z))
                                {
                                    if(shading)
                                    {
                                        phongShading(gl,poly.getColor(), new Point(wX,wY,wZ,1), new Point(nX,nY,nZ,1), lookFrom);
                                    }
                                    else
                                    {
                                        gl.glColor3f(poly.getColor().getRed(),poly.getColor().getGreen(),poly.getColor().getBlue());
                                    }
                                    gl.glVertex3f(X, Y, 0);
                                }
                                }
                                z += zmx;
                                nX += mnX;
                                nY += mnY;
                                nZ += mnZ;
                                
                                float len = (float)Math.sqrt(nX*nX+nY*nY+nZ*nZ);
                                nX = nX/len;
                                nY = nY/len;
                                nZ = nZ/len;
                                
                                wX += mwX;
                                wY += mwY;
                                wZ += mwZ;
                            }
                        }
                        draw = !draw;

                    }

                    for (Edge e : AET)
                    {
                        e.incrementCurrentX();
                        e.incrementCurrentZ();
                        e.incrementNormal();
                        e.normalizeNormals();
                        e.incrementWorld();
                    }

                }

                Y = Y + 1;

            }

        } catch (Exception e)
        {
            e.printStackTrace();
        }

    }
    
    private void phongShading(GL gl,Color polyC,Point pix, Point n, Point lookFrom)
    {
       // Point lookFrom = new Point(0,0,100,1);
        
        Point e = lookFrom.minus(pix);
        
        Point l = new Point(1,1,1,1);
        
        l.normalize();
        e.normalize();
        n.normalize();
        float Ca = (float).6;//.6;
        float Cl = (float).9;
        float Cp = (float)1;
        float p = 8;
        //Cr(Ca + Cl max(0, n.l))+ ClCpmax(0, e.r)p
        //r = 2n(n.l)-l
        Point r = n.times(2).times(n.dot(l)).minus(l);
        r.normalize();
        l.normalize();
        e.normalize();
        n.normalize();
        //System.out.prinln()
        
        float last = Cl*Cp*(float)Math.pow(max(0,e.dot(r)),p);
        float first = (Ca + Cl * max(0,n.dot(l)));
        
        float red = polyC.getRed()*first + last;//*polyC.getRed();
        float green = polyC.getGreen()*first + last;//*polyC.getGreen();
        float blue = polyC.getBlue()*first + last;//*polyC.getBlue();
        
        if(red > 1)
        {
            red = 1;
        }
        if(blue > 1)
        {
            blue = 1;
        }
        if(green > 1)
        {
            green = 1;
        }
        gl.glColor3f(red,green,blue);
        //gl.glColor3f(polyC.getRed(),polyC.getGreen(),polyC.getBlue());
    }
    
    private float max(float a, float b)
    {
        return (b > a ? b : a);
    }
    private boolean getIsEmpty(List<Edge>[] get)
    {
        boolean empty = true;
        for (List l : get)
        {
            if (!l.isEmpty())
            {
                empty = false;
            }
        }
        return empty;
    }
}